package cn.wolfcode.shop.service.impl;

import cn.wolfcode.shop.common.BusinessException;
import cn.wolfcode.shop.common.Result;
import cn.wolfcode.shop.domain.Good;
import cn.wolfcode.shop.domain.SeckillGood;
import cn.wolfcode.shop.mapper.SeckillGoodMapper;
import cn.wolfcode.shop.msg.SeckillServerCodeMsg;
import cn.wolfcode.shop.service.ISeckillGoodService;
import cn.wolfcode.shop.util.RedisKeys;
import cn.wolfcode.shop.vo.SeckillGoodVO;
import cn.wolfcode.shop.web.feign.GoodFeignApi;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;

@Service
public class SeckillGoodServiceImpl implements ISeckillGoodService {

    @Autowired
    private SeckillGoodMapper seckillGoodMapper;

    @Autowired
    private GoodFeignApi goodFeignApi;      // 涉及RPC 检查启动类有无注解：@EnableFeignClients

    @Autowired
    private StringRedisTemplate redisTemplate;

    //查秒杀商品列表
    @Override
    public List<SeckillGoodVO> query() {
        // 秒杀商品列表的查询
        // 1.查询商品秒杀的列表
        List<SeckillGood> seckillGoodList = seckillGoodMapper.listAll();
        List<SeckillGoodVO> seckillGoodVOList = getVo(seckillGoodList);
        return seckillGoodVOList;
    }

    //  秒杀商品的通过Redis查
    @Override
    public List<SeckillGoodVO> queryByCache() {
        // 秒杀商品列表的查询
        // 1.查询商品秒杀的列表
        // 查询Redis中hash的value
        List<Object> vos = redisTemplate.opsForHash().values(RedisKeys.SECKILL_GOOD_HASH.join());
        if (!CollectionUtils.isEmpty(vos)) {
            ArrayList<SeckillGoodVO> voList = new ArrayList<>(vos.size());  // 封装vo的list
            // 遍历哈希value的集合
            for (Object vo : vos) {
                SeckillGoodVO seckillGoodVO = (SeckillGoodVO) vo;

                // vo的库存信息在查询之前要更新同步，通过hash查询到库存信息，set进vo中
                String stockCountStr = (String) redisTemplate.opsForHash()
                        .get(RedisKeys.SECKILL_GOOD_STOCKCOUNT_HASH.join(), seckillGoodVO.getId() + "");
                // 库存
                Integer stockCount = Integer.parseInt(stockCountStr);
                stockCount = StringUtils.isEmpty(stockCountStr) || stockCount < 0 ? 0 : stockCount;
                seckillGoodVO.setStockCount(stockCount);    //设置库存

                voList.add(seckillGoodVO);
            }
            return voList;
        }
        return null;
    }


    // 秒杀商品详情的查询 通过Redis的缓存查询
    @Override
    public SeckillGoodVO findByCache(Long id) {
        String seckillGoodVOStr = (String) redisTemplate.opsForHash()
                .get(RedisKeys.SECKILL_GOOD_HASH.join(), id + "");
        SeckillGoodVO seckillGoodVO = JSON.parseObject(seckillGoodVOStr,SeckillGoodVO.class);

        // vo的库存信息在查询之前要更新同步，通过hash查询到库存信息，set进vo中
        String stockCountStr = (String) redisTemplate.opsForHash()
                .get(RedisKeys.SECKILL_GOOD_STOCKCOUNT_HASH.join(), id + "");
        // 库存
        Integer stockCount = Integer.parseInt(stockCountStr);
        stockCount = StringUtils.isEmpty(stockCountStr) || stockCount < 0 ? 0 : stockCount;
        seckillGoodVO.setStockCount(stockCount);    //设置库存

        return seckillGoodVO == null ? null : seckillGoodVO;
    }


    //通过 id 查秒杀商品
    @Override
    public SeckillGoodVO findById(Long seckillGoodId) {
        SeckillGood seckillGood = seckillGoodMapper.selectByPrimaryKey(seckillGoodId);
//        List<SeckillGood> seckillGoodList = new ArrayList<>(1);
//        seckillGoodList.add(seckillGood);
        List<SeckillGood> seckillGoodList = Collections.singletonList(seckillGood);
        List<SeckillGoodVO> seckillGoodVOList = getVo(seckillGoodList);
        return seckillGoodVOList.size() > 0 ? seckillGoodVOList.get(0) : null;
    }


    @Override
    public int decrStockCount(Long seckillId) {
        //返回受影响的行数
        return seckillGoodMapper.decrStockCount(seckillId);
    }

    // 抽取出来的查询秒杀商品的方法，对 query 和 findById 复用
    private List<SeckillGoodVO> getVo(List<SeckillGood> seckillGoodList) {
        // 2.根据商品秒杀的列表，得到商品 id 的列表, ids 应该是Set集合，因为不同场秒杀关联的商品可能相同
        Set<Long> goodIds = new HashSet<>(seckillGoodList.size());
        for (SeckillGood seckillGood : seckillGoodList) {
            goodIds.add(seckillGood.getId());
        }

        // 3.通过 feign 远程调用商品服务，查询列表
        // TODO  商品服务的远程调用，通过 feign 暴露的接口
        Result<List<Good>> result = goodFeignApi.getGoodListByIds(goodIds);
        // 商品服务可能返回三种结果：1. 正常返回；    2. 降级的null； 3. code != 200 返回result
        if (result.hasError()) {    // result == null || result.getCode() != 200 的情况是有错误
            // 返回 null 或者抛出异常给调用者响应的提示
            throw new BusinessException(SeckillServerCodeMsg.DEFAULT_ERROR);
        }
        //正常调用返回的数据
        List<Good> goodList = result.getData();

        // 通过遍历的方式来给属性赋值，效率低。采用HashMap做缓存的方式来操作
        HashMap<Long, Good> tempCache = new HashMap<>(goodList.size());
        // 把查到的列表先丢到map中
        for (Good good : goodList) {
            tempCache.put(good.getId(), good);
        }
        // 4.遍历商品列表，将列表中每项的属性，复制到 VO 中，得到 vo 列表
        List<SeckillGoodVO> seckillGoodVOList = new ArrayList<>(seckillGoodList.size());
        for (SeckillGood seckillGood : seckillGoodList) {
            Good good = tempCache.get(seckillGood.getGoodId());// 拿到map中的值
            SeckillGoodVO vo = new SeckillGoodVO();     // 创建vo对象，复制两者属性

            // ------ 注意属性复制的顺序：vo的id应该和秒杀商品的id相同 -------
            // * 复制商品的属性
            if (good != null)
                //有值才复制属性：BeanUtils
                BeanUtils.copyProperties(good, vo);
            // * 复制秒杀商品的属性
            BeanUtils.copyProperties(seckillGood, vo);

            seckillGoodVOList.add(vo);  // 添加进list中
        }
        return seckillGoodVOList;
    }

}
